#include <vector>
//The real tragedy of this bundle of joy is the Expression class. If we find a way aside from vector lists
//then this whole group will be improved.

/*Expressions occur in two places in parametric L-Systems: as logical conditions in parametric productions, and as arithmetic operations in production successors.
	This implementation is ugly. We define an Operator type by enumeration and use it to guide a switch operation during production, but its fucking ugly and we
	don't even use the ABBREVIATED names. This makes the operator an integer. The main operand always corresponds to a parameter, denoted by its position in a list of
	parameters of a module in a production predecessor (parameters are defined by an ordered list in parametric L-Systems so indexing this way is justifiable but not clear).
	The second operand can be another parameter, and thus another integer index, or a constant value given by a float parameter. The boolean states which of these is the case
	again, for the benefit of the switch statement. I gotta figure out what the deal with these switch statements is. Really don't like having to do this.*/
class Expression
{
	public:
		int oper;
		int operand;			//The condition symbol should correspond to the position of the relevant parameter in the parameter list
		int secondary;
		float conditionVal;
		bool constant;

		enum Operator{eq, gt, lt, gte, lte, plus, minus, mul, div};

		Expression();
		Expression(int, int, int, float, bool);
};

/*A module is a serious complication of what is otherwise a char in regular L-Systems. Modules occur in two places: within Parametric_L_System symbol lists for
turtle analysis and production, and as the components of productions in Parametric L_Systems. Modules under turtle analysis and production require a list of 
real-valued parameters. Modules within productions require a (possibly empty) list of arithmetic expressions. This is a waste of storage in order to have a unified
class that are very similar in functionality.*/
class Module
{
	public:
		char symbol;
		std::vector<float> parameters;
		std::vector<Expression> expressions;
	
		Module();
		Module(char);
		Module(char, std::vector<float>);
		Module(char, std::vector<Expression>);
};
		
/*A Production consists of three parts: a predecessor, which is a single Module denoted by a character and a number of parameters that predecessor carries;
a condition, which is a single expression consisting of logical operators that evaluates to true or false to determine production application;
and a list of Modules which are the successors of the predecessor. The Production class analyzes its objects with two methods. 

checkCondition ensures the predecessor matches a given module under scrutiny, both by checking the symbol and number of parameters against the predecessor, and
by evaluating the condition. 

applyProduction replaces the module under scrutiny with the successor, evaluating arithmetic expressions in the process and updating module values.*/
class Production
{
	public:
		char symbol;
		int numParameters;					//The number of parameters in the production predecessor	strictly:(condSymbol < parameters)
		Expression condition;
										
		std::vector<Module> successors;

		Production();
		Production(char, int, Expression, std::vector<Module>);
		bool checkCondition(Module m);
		std::vector<Module> applyProduction(Module m);
};
